package com.lagou.niancheng.core.session.defaults;

import com.lagou.niancheng.core.config.StatementType;
import com.lagou.niancheng.core.exceptions.LagouMybatisException;
import com.lagou.niancheng.core.config.Configuration;
import com.lagou.niancheng.core.config.MappedStatement;
import com.lagou.niancheng.core.session.Executor;
import com.lagou.niancheng.core.session.MapperProxy;
import com.lagou.niancheng.core.session.SqlSession;
import com.lagou.niancheng.core.transaction.TransactionManager;

import java.lang.reflect.*;
import java.text.MessageFormat;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    private Executor executor;
    private TransactionManager transactionManager;

    public DefaultSqlSession(Configuration configuration,
                             Executor executor,
                             TransactionManager transactionManager) {
        this.configuration = configuration;
        this.executor = executor;
        this.transactionManager = transactionManager;
    }

    @Override
    public Object executeStatement(Type genericReturnType, String statementId, Object... params) throws Exception {
        MappedStatement mappedStatement = getStatement(statementId);
        StatementType statementType = mappedStatement.getStatementType();
        switch (statementType) {
            case DELETE:
                delete(statementId, params);
                break;
            case INSERT:
                insert(statementId, params);
                break;
            case UPDATE:
                update(statementId, params);
                break;
            case SELECT:
                // 判断是否进行了 泛型类型参数化
                if (genericReturnType instanceof ParameterizedType) {
                    return selectList(statementId, params);
                }
                return selectOne(statementId, params);
            default:
                break;
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <E> List<E> selectList(String statementId, Object... params) throws Exception {
        //将要去完成对simpleExecutor里的query方法的调用
        MappedStatement mappedStatement = getStatement(statementId);
        List<Object> list = executor.query(mappedStatement, params);
        return (List<E>) list;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new LagouMybatisException("result too many or empty");
        }
    }

    @Override
    public void commit() {
        transactionManager.commit();
    }

    @Override
    public void rollback() {
        transactionManager.rollback();
    }

    @Override
    public void update(String statementId, Object... params) throws Exception {
        MappedStatement statement = getStatement(statementId);
        executor.update(statement, params);
    }

    @Override
    public void insert(String statementId, Object... params) throws Exception {
        MappedStatement statement = getStatement(statementId);
        executor.insert(statement, params);
    }

    @Override
    public void delete(String statementId, Object... params) throws Exception {
        MappedStatement statement = getStatement(statementId);
        executor.delete(statement, params);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getMapper(Class<?> mapperClass) {
        if (!mapperClass.isInterface()) {
            throw new LagouMybatisException(MessageFormat.format("not support proxy for this class: {0}",
                    mapperClass.getName()));
        }
        // 使用JDK动态代理来为Dao接口生成代理对象，并返回
        MapperProxy mapperProxy = new MapperProxy(this);
        return (T) Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, mapperProxy);
    }

    private MappedStatement getStatement(String statementId) {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        if (mappedStatement == null) {
            throw new LagouMybatisException("cannot find statement: " + statementId);
        }
        return mappedStatement;
    }

}
